# encoding: utf-8
"""
UCloud resources
============
"""

import logging, requests
import sys

from flask_restplus import Resource
import sqlalchemy
import traceback

from flask_restplus_patched import Parameters
from app.extensions import db
from app.extensions.api import Namespace, abort, http_exceptions
from app.extensions.api.parameters import PaginationParameters
from app.modules.common.pipelines import PipelineNotFoundError
from app.modules.zones.models import Zone
from config import BaseConfig

log = logging.getLogger(__name__)  # pylint: disable=invalid-name
api = Namespace('zones', description = "zones")  # pylint: disable=invalid-name

from . import parameters, schemas
from .models import Zone
from app.modules.database.pipelines_factory import DatabasePipelinesFactory
from app.modules.common import pipeline_manager
from app.modules.apps.models import App


@api.route('/')
class Zones(Resource):
    @api.parameters(parameters.ZonesParameters())
    @api.response(schemas.BaseZonesSchema(many = True))
    def get(self, args):
        """
        List of teams.

        Returns a list of teams starting from ``offset`` limited by ``limit``
        parameter.
        """
        log.info("============== start query zones ==============")
        log.info("============== param[available] is %s", args['available'])

        # try:
        #     try:
        #         zone = Zone(zone_code="USR", parent_code=None)
        #     except ValueError as exception:
        #         abort(code=http_exceptions.Conflict.code, message=str(exception))
        #     db.session.add(zone)
        #     try:
        #         db.session.commit()
        #     except sqlalchemy.exc.IntegrityError:
        #         abort(code=http_exceptions.Conflict.code, message="Could not create a new Zone.")
        # finally:
        #     db.session.rollback()
        query = Zone.query
        if args['available'] is not None:
            return query.filter_by(available = int(args['available'])).order_by(
                Zone.order_no.asc())

        return query.filter_by(available = True).order_by(Zone.order_no.asc())


@api.route('/install')
class ZonesInstall(Resource):
    @api.parameters(parameters.ZonesInstallParamters())
    @api.response(code = http_exceptions.Conflict.code)
    def put(self, args):

        log.info("============== start execute zones install ==============")

        only_db = args['only_db']
        data_from = args['data_from']
        zones = args['zones']
        if zones is None:
            _zone_list = Zone.query.all()
            for _z in _zone_list:
                zones = '%s,%s' % (zones, _z.zone_code)

        log.info("============== install zones [%s] ==============",
                 zones)
        log.info("============== install data_from [%s] ==============",
                 data_from)
        log.info("============== install only_db [%s] ==============", only_db)

        zones = zones.split(',')

        if data_from and data_from == 'product':
            is_product = True
            need_db = True
        elif data_from and data_from == 'demo':
            is_product = False
            need_db = True
        else:
            is_product = False
            need_db = False

        start_dbs = []
        for zone in zones:
            apps = App.query.filter_by(disabled = 0).filter_by(zone_code = str(zone)).order_by(
                App.starting_order.asc()).all()
            if apps:

                # stop app pipelines
                # if not only_db:
                # for app in apps:
                #     if (app.app_type != 'php') and (str(app.status).lower() != 'unavailable'):
                #         pipeline_name = app.app_id + '_stop'
                #         log.info('***> Start submit pipeline : %s', pipeline_name)
                #         pipeline_manager.start_pipeline(pipeline_name=pipeline_name)

                # start db pipelines
                if need_db:
                    for app in apps:
                        if app.db_name and app.db_type:
                            key = str(app.db_name + ':' + app.db_type)
                            if key not in start_dbs:
                                start_dbs.append(key)
                                pipeline_name = DatabasePipelinesFactory.get_pipeline_name(
                                    app.app_id,
                                    app.db_type,
                                    app.db_name,
                                    is_product = is_product)
                                log.info('***> Start submit pipeline : %s',
                                         pipeline_name)
                                pipeline_manager.start_pipeline(
                                    pipeline_name = pipeline_name)

                                # start app pipelines
                                # if not only_db:
                                # for app in apps:
                                #     pipeline_name = app.app_id + '_install'
                                #     log.info('***> Start submit pipeline : %s', pipeline_name)
                                #     pipeline_manager.start_pipeline(pipeline_name=pipeline_name)
                                #     pipeline_name = app.app_id + '_restart'
                                #     log.info('***> Start submit pipeline : %s', pipeline_name)
                                #     pipeline_manager.start_pipeline(pipeline_name=pipeline_name)

        return None


@api.route('/sync')
class Sync(Resource):
    @api.parameters(Parameters())
    @api.response(code = http_exceptions.Conflict.code)
    def get(self, args):
        syncZoneAndApp()
        return None


def syncZoneAndApp():
    log.info("============== start sync zone and app  ==============")
    # 1.请求ucloud-master，获取最新的zone和app信息
    # 2.根据获取的zone和app列表，判断本机数据库中是否存在记录，不存在则新增，存在pass
    # zone
    _zone_url = '%s/zone/' % BaseConfig.UCLOUD_MASTER_URL
    _zone_obj = requests.get(url = _zone_url).json()
    for _zone in _zone_obj['data']:
        log.info('sync zone : %s ' % _zone)
        _z = Zone.query.filter_by(zone_code = _zone['zone_code']).first()
        if _z is None:
            _z = Zone(zone_code = _zone['zone_code'], parent_code = _zone['parent_code'],
                      zone_title = _zone['zone_title'], zone_desc = _zone['zone_desc'], available = _zone['available'],
                      home_path = _zone['home_path'], build_in = _zone['build_in'], timezone = _zone['timezone'],
                      zone_alias = _zone['zone_alias'], order_no = _zone['order_no'])
            db.session.add(_z)
            db.session.commit()
    # 删除本地有，但是远程没有的zone
    _isCom = False
    for _lzone in Zone.query.all():
        _isDel = True
        for _zone in _zone_obj['data']:
            if _lzone.zone_code == _zone['zone_code']:
                _isDel = False
                break
        if _isDel:
            db.session.delete(_lzone)
            _isCom = True
    if _isCom:
        db.session.commit()

    # app
    _app_url = '%s/app/' % BaseConfig.UCLOUD_MASTER_URL
    _app_obj = requests.get(url = _app_url).json()
    for _app in _app_obj['data']:
        log.info('sync app : %s ' % _app)
        _a = App.query.filter_by(app_id = _app['app_id']).first()
        if _a is None:
            _a = App(app_id = _app['app_id'], zone_code = _app['zone_code'], app_name = _app['app_name'],
                     port = _app['port'], app_type = _app['app_type'], db_name = _app['db_name'],
                     db_type = _app['db_type'], status = 'unavailable', version = '-1', on_beetle = _app['on_beetle'],
                     db_status = 'unavailable', mem_level = _app['mem_level'], disabled = _app['disabled'],
                     starting_order = _app['starting_order'])
            db.session.add(_a)
            db.session.commit()
        else:
            # 修改应用的
            _a.mem_level = _app['mem_level']
            _a.disabled = _app['disabled']
            _a.db_name = _app['db_name']
            _a.db_type = _app['db_type']
            db.session.commit()

    # 删除本地有，但是远程没有的zone
    _isCom = False
    for _lapp in App.query.all():
        _isDel = True
        for _app in _app_obj['data']:
            if _lapp.app_id == _app['app_id']:
                _isDel = False
                break
        if _isDel:
            db.session.delete(_lapp)
            _isCom = True
    if _isCom:
        db.session.commit()
